﻿/*=========================================================================
 * AUTH: Vincent Breton
 * DESC: handles the Formation
 *=========================================================================*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace RisingThreat
{
    class FormationManager
    {
        private Player player;
        private List<Teammate> team;
        private Vector3 anchor;
        private Vector3 avgPosition;
        private float offset;
        private Vector3 avgVelocity;
        private Vector3 teamSlot1;
        private Vector3 teamSlot2;


        public FormationManager(Player p, List<Teammate> t)
        {
            player = p;
            team = t;
            offset = 2.0f;
   
        }

        public void updateFormation(Level level)
        {
            /*Horizontal Line Formation
             * team1  player  team2
             *        
             * */
            float orientation = player.steer.orientation;
            Vector3 vectorOffset = player.steer.position + new Vector3(offset * (float)Math.Cos(orientation + MathHelper.PiOver2), 0.2f, offset * (float)Math.Sin(orientation + MathHelper.PiOver2));
            Vector3 vectorOffset2 = player.steer.position + new Vector3(offset * (float)Math.Cos(orientation - MathHelper.PiOver2), 0.2f, offset * (float)Math.Sin(orientation - MathHelper.PiOver2));

            if (orientation < 1.57)
            {
                teamSlot1 = vectorOffset2;
                teamSlot2 = vectorOffset;
            }
            else
            {
                teamSlot1 = vectorOffset;
                teamSlot2 = vectorOffset2;
            }
            if (invalidSlotPosition(level))
            {/*revert to Vertical Line Formation 
              *     player
              *     Team1
              *     Team2
              *     
              * */
                Vector3 alternateVector1 = player.steer.position + new Vector3(offset * (float)Math.Cos(-orientation), 0.2f, offset * (float)Math.Sin(-orientation));
                Vector3 alternateVector2 = player.steer.position + new Vector3(3 * offset * (float)Math.Cos(-orientation), 0.2f, 3 * offset * (float)Math.Sin(-orientation));
                teamSlot1 = alternateVector1;
                teamSlot2 = alternateVector2;
               /* if (orientation > 1.57)
                {
                    teamSlot1 = alternateVector1;
                    teamSlot2 = vectorOffset;
                }
                else
                {
                    teamSlot1 = vectorOffset;
                    teamSlot2 = vectorOffset;
                }*/
            }

        }

        /*checks if slot position is invalid*/
        public bool invalidSlotPosition(Level level)
        {
            Vector3 direction = teamSlot1-team[0].steer.position;
            direction.Normalize();
            Ray rayCast = new Ray(team[0].steer.position, direction);

            // Detect intersections by parallel ray
            HashSet<KeyValuePair<QuadTreeItem, float?>> leftIntersections = level.getRayIntersections(rayCast);

            float distance=0;
            // IF there are any intersections with rays
            if (leftIntersections.Count > 0)
            {
                // Get closest intersecting object
                distance= (float) getClosestIntersection(leftIntersections);
                        return true;
            }

            
            Vector3 direction1 = teamSlot2-team[1].steer.position;
            direction1.Normalize();
            Ray rayCast1 = new Ray(team[1].steer.position, direction1);
            // Detect intersections by parallel ray
            HashSet<KeyValuePair<QuadTreeItem, float?>> rightIntersections = level.getRayIntersections(rayCast1);

            float distance1 = 0;
            // IF there are any intersections with rays
            if (leftIntersections.Count > 0)
            {
                // Get closest intersecting object
                distance1 = (float) getClosestIntersection(leftIntersections);
                        return true;
            }

            return false;
        }
        // Given a number of ray intersections, find the closest one
        private float getClosestIntersection(HashSet<KeyValuePair<QuadTreeItem, float?>> intersections)
        {
            // Move intersecting objects to an array
            KeyValuePair<QuadTreeItem, float?>[] interArray = new KeyValuePair<QuadTreeItem, float?>[intersections.Count];
            intersections.CopyTo(interArray);

            // Closes intersection found
            KeyValuePair<QuadTreeItem, float?> closest = interArray[0];

            float distance = 1000;
            // Iterate over array, and find min distance
            for (int i = 1; i < interArray.Length; ++i)
            {
                if (interArray[i].Value < closest.Value)
                {
                    closest = interArray[i];
                    distance = (float)interArray[i].Value;
                }
            }

            // Return closest item
            return distance;
        }
      

        public Vector3 getAveragePosition()
        {
            Vector3 avg = player.steer.position;
            int count = 0;
            if (team[0].isAlive)
            {
                avg += team[0].steer.position;
                count++;
            }
            if (team[1].isAlive)
            {
                avg += team[1].steer.position;
                count++;
            }
            avg /= count + 1;

            return avg;
        }

        public Vector3 getAverageVelocity()
        {
            Vector3 avg = player.steer.velocity;
            int count = 0;
            if (team[0].isAlive)
            {
                avg += team[0].steer.velocity;
                count++;
            }
            if (team[1].isAlive)
            {
                avg += team[1].steer.velocity;
                count++;
            }
            avg /= count + 1;

            return avg;
        }

        public void Update(Level level)
        {
            /*avgPosition = getAveragePosition();
            avgVelocity = getAverageVelocity();

            anchor = avgPosition + offset * avgVelocity;

            Vector3 deltaSlot1 = teamSlot1 - avgPosition;
            Vector3 deltaSlot2 = teamSlot2 - avgPosition;

            teamSlot1 = anchor + deltaSlot1;
            teamSlot2 = anchor + deltaSlot2;
            */
            updateFormation(level);
            team[0].seek.target = teamSlot1;
            team[1].seek.target = teamSlot2;
        }
    }
}
